package com.lh.api.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.JsonArray;
import com.lh.api.config.LHConfig;
import com.lh.api.dao.*;
import com.lh.api.service.DoboCodeService;
import com.lh.api.service.PayService;
import com.lh.api.vo.PayConfigVo;
import com.lh.api.vo.VerifyVo;
import com.lh.common.entity.admin.*;
import com.lh.common.entity.state.RemarkEnum;
import com.lh.common.enums.*;
import com.lh.common.utils.AssertUtil;
import com.lh.common.utils.Constant;
import com.lh.common.utils.DateTimeUtil;
import com.paypal.base.codec.binary.Base64;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


@Service
@Slf4j
public class PayServiceImpl extends ServiceImpl<OrderMapper, Order> implements PayService {
	private static final Lock LOCKCODE= new ReentrantLock();

	private static final String TRANSACTIONSKEY ="transactions";

	@Autowired
	private UserAddressMapper userAddressMapper;

	@Autowired
	private PeriodMapper periodMapper;

	@Autowired
	private ShopMapper shopMapper;

	@Autowired
	private ActivityShopOneMapper activityShopOneMapper;

	@Autowired
	private DoboCodeMapper doboCodeMapper;

	@Autowired
	private DoboCodeService doboCodeService;

	@Autowired
	private OrderStatusLogMapper orderStatusLogMapper;

	@Autowired
	private OrderShopMapper orderShopMapper;

	@Autowired
	private OrderAfterSaleMapper orderAfterSaleMapper;

	@Autowired
	private OrderAfterSaleLogMapper orderAfterSaleLogMapper;

	@Autowired
	private LHConfig lhConfig;

	@Autowired
	private APIContext apiContext;

	public static final String PAYPAL_SUCCESS_URL = "/api/pay/success";

	public static final String PAYPAL_CANCEL_URL = "/api/pay/cancel";

	private boolean updateOrderSuccess(Order order, String saleId, String paymentId) {
		boolean flag = false;
		try {
			Order orderUpdate = new Order();
			orderUpdate.setOid(order.getOid());
			orderUpdate.setOrderStatus(OrderStatusEnum.TOSHIP);
			orderUpdate.setUpdateTime(new Date());
			//支付成功信息
			orderUpdate.setPayType(PayType.PAYPAL);
			orderUpdate.setPayCallBackTime(new Date());
			orderUpdate.setPayTime(new Date());
			orderUpdate.setPayPaymentsId(saleId);
			orderUpdate.setPayOrderNumber(paymentId);

			//根据PID获取相关的期数
			if(OrderTypeEnum.ONEDOLLARRUSH.equals(order.getOrderType())){
				Period period = periodMapper.selectById(order.getPeriodId());
				Period periodRecord = new Period();

				//判断是否过期
				AssertUtil.assertNotTrue(PeriodStatusEnum.REVEALED.equals(period.getStatus())|| IsDel.YES.equals(period.getIsDel()),
						Constant.RESULT.FI1009, "Activity has expired and cannot be purchased");
				//生成夺宝码和扣减库存在同一把锁中  先生成夺宝码再减库存
				LOCKCODE.lock();
				//生成夺宝码
				try {
					//更新ovmal_dobo_code夺宝码
					doboCodeService.updateByOrderItem( order.getOrderItem(), order.getPeriodId(), order.getShopNum());

					//扣减库存 11类型的当库存小于0则设置为0
					periodRecord.setCurrenPortion(period.getCurrenPortion() + order.getShopNum());
					if(period.getTotalPortion() <= order.getShopNum()){
						periodRecord.setTotalPortion(0);
					}else{
						periodRecord.setTotalPortion(period.getTotalPortion() - order.getShopNum());
					}

					//扣减库存
					periodRecord.setPid(period.getPid());
					periodRecord.setUpdateTime(new Date());
					periodMapper.updateByKey(periodRecord);
				} finally {
					LOCKCODE.unlock();
				}
				orderUpdate.setOrderStatus(OrderStatusEnum.COMPLETED);
			}
			addStatusLog(order.getUserId(), order.getOid(), order.getOrderStatus(), OrderStatusEnum.TOSHIP, RemarkEnum.SHZT12);
			baseMapper.updateByKey(orderUpdate);//更新ORDER订单
			flag = true;
		} catch (Exception e) {
			flag = false;
			log.error("doOrderSuccess 异常："+e);
		}
		return flag;
	}

	/*验证不成功，订单修改成待退款,记录LOG*/
	public void doOrderFail(String saleId,String paymentId, Order order, Boolean payResult, String userId) {
		//订单中如果有第三方支付ID说明支付过了，不需要处理了
		if(StringUtils.isNotEmpty(order.getPayPaymentsId())){
			return;
		}

		Order orderUpdate = new Order();
		orderUpdate.setOid(order.getOid());
		orderUpdate.setUpdateTime(new Date());
		//支付相关信息
		orderUpdate.setPayType(PayType.PAYPAL);
		orderUpdate.setPayCallBackTime(new Date());
		orderUpdate.setPayTime(new Date());
		orderUpdate.setPayPaymentsId(saleId);
		orderUpdate.setPayOrderNumber(paymentId);

		//记录订单修改状态
		if(payResult){
			addStatusLog(Long.parseLong(userId), order.getOid(), order.getOrderStatus(), OrderStatusEnum.ACTIVITYLOSE, RemarkEnum.SHZT17);
			orderUpdate.setOrderStatus(OrderStatusEnum.ACTIVITYLOSE);
		}else {
			addStatusLog(Long.parseLong(userId), order.getOid(), order.getOrderStatus(), OrderStatusEnum.PAYCANCEL, RemarkEnum.SHZT23);
			orderUpdate.setOrderStatus(OrderStatusEnum.PAYCANCEL);
		}
		baseMapper.updateByKey(orderUpdate);//更新ORDER订单
	}

	private void addStatusLog(Long userId, Long oid, OrderStatusEnum oldStatus, OrderStatusEnum newStatus, String remark) {
		OrderStatusLog orderStatusLog = new OrderStatusLog();
		orderStatusLog.setUserId(userId);
		orderStatusLog.setOrderId(oid);
		orderStatusLog.setOldStatus(oldStatus);
		orderStatusLog.setNewStatus(newStatus);
		orderStatusLog.setCreateTime(new Date());
		orderStatusLog.setUpdateTime(new Date());
		orderStatusLog.setRemark(remark);
		orderStatusLogMapper.insert(orderStatusLog);
	}

	@Override
	public PayConfigVo getPayConfig(String orderItem,Long userId, HttpServletRequest request) {
		PayConfigVo payConfigVo = new PayConfigVo();
		payConfigVo.setClientId(lhConfig.getPaypal_client_id());

		//检查订单状态
		QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq(Constant.ORDER_ITEM, orderItem).eq(Constant.USER_ID,userId+"");
		Order order = baseMapper.selectOne(queryWrapper);

		//判断订单是否过期了
		AssertUtil.assertNotNull(order, Constant.RESULT.FI1000, "This order does not exist");
		AssertUtil.assertNotTrue(!order.getUserId().equals(userId), Constant.RESULT.FI1000, "User information does not match");
		AssertUtil.assertNotTrue(!OrderStatusEnum.TOPAY.equals(order.getOrderStatus()),
				Constant.RESULT.FI1009, "This order's status is not pending payment,so you can not pay for it" );
		AssertUtil.assertNotTrue(OrderStatusEnum.CANCELLED.equals(order.getOrderStatus()), Constant.RESULT.FI1009, "The order has expired and cannot be paid");

		if(OrderTypeEnum.ONEDOLLARRUSH.equals(order.getOrderType())){
			AssertUtil.assertNotTrue((long) DateTimeUtil.getRemainingTime(new Date(order.getCreateTime().getTime()+1000*60*5)) == 0l,
					Constant.RESULT.FI1009, "Unpaid for more than 5 minutes, unable to pay now" );

			//根据PID获取相关的期数
			Period period = periodMapper.selectById(order.getPeriodId());
			//判断是否过期
			AssertUtil.assertNotTrue(PeriodStatusEnum.REVEALED.equals(period.getStatus())|| IsDel.YES.equals(period.getIsDel()),
					Constant.RESULT.FI1009, "Activity has expired and cannot be purchased");
			ActivityShopOne activityShopOne = activityShopOneMapper.selectById(order.getAid());
			payConfigVo.setShopName(checkName(activityShopOne.getShopName()));
		}else {
			AssertUtil.assertNotTrue((long) DateTimeUtil.getRemainingTime(new Date(order.getCreateTime().getTime()+1000*60*15)) == 0l,
					Constant.RESULT.FI1009, "Unpaid for more than 15 minutes, unable to pay now" );

			QueryWrapper<OrderShop> queryWrapper1 = new QueryWrapper<>();
			queryWrapper1.eq(Constant.ORDER_ITEM, order.getOrderItem()).eq("sort", 0);
			OrderShop orderShop = orderShopMapper.selectOne(queryWrapper1);
			payConfigVo.setShopName(checkName(orderShop.getShopName()));
		}
		payConfigVo.setAmount(order.getPayPrice());
		payConfigVo.setCurrency("USD");

		return payConfigVo;
	}

	private String checkName(String shopName) {
		if(StringUtils.isBlank(shopName)){
			return shopName;
		}
		if(shopName.length()>20){
			return shopName.substring(0,17)+ "...";
		}
		return  shopName;
	}
	@Override
	@Transactional
	public VerifyVo success(String paymentId, String orderItem, Long userId) throws PayPalRESTException {
		Boolean payResult = false;
		VerifyVo result = new VerifyVo();
		//校验订单
		QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq(Constant.ORDER_ITEM, orderItem).eq(Constant.USER_ID,userId);
		Order order = baseMapper.selectOne(queryWrapper);
		AssertUtil.assertNotNull(order, Constant.RESULT.FI1000, "This order does not exist");
		AssertUtil.assertNotTrue(!userId.equals(order.getUserId()), Constant.RESULT.FI1000, "User information does not match");

		//校验支付是否成功
		String str = getPaymentDetails(paymentId);
		if(str == null ){
			//查询不到信息订单变为支付中
			Order orderUpdate = new Order();
			orderUpdate.setOid(order.getOid());
			orderUpdate.setUpdateTime(new Date());
			orderUpdate.setOrderStatus(OrderStatusEnum.INPAY);
			baseMapper.updateByKey(orderUpdate);//更新ORDER订单
			addStatusLog(userId, order.getOid(), order.getOrderStatus(), OrderStatusEnum.INPAY, RemarkEnum.SHZT11);
			result.setResult(payResult);
			return result;
		}
		//解析数据
		JSONObject detail = JSONObject.fromObject(str);
		if("approved".equals(detail.optString("state"))){
			payResult = true;
		}
		JSONObject transaction = detail.getJSONArray(TRANSACTIONSKEY).getJSONObject(0);
		String saleId = transaction.getJSONArray("related_resources").getJSONObject(0).getJSONObject("sale").getString("id");
		log.info("success,paymentId:{},orderItem:{},payResult:{},saleId:{}",paymentId, orderItem, payResult, saleId);

		boolean flag = false;
		if(payResult ){//验证成功，判断商品库存情况等，生成夺宝码,记录log表
			flag = updateOrderSuccess(order, saleId, paymentId);
			if(!flag){//如果出现异常，则修改成待退款
				doOrderFail(saleId,paymentId,order,payResult, userId + "");
			}
		}else{//验证不成功，订单修改成待退款,记录LOG
			doOrderFail(saleId,paymentId,order,payResult, userId + "");
		}

		//生成夺宝码
		if(flag){
			QueryWrapper<DoboCode> queryWrapper2 = new QueryWrapper<>();
			queryWrapper2.eq(Constant.ORDER_ITEM, orderItem);
			List<DoboCode> doboCodes = doboCodeMapper.selectList(queryWrapper2);

			if(!CollectionUtils.isEmpty(doboCodes)){
				result.setCodeList(Arrays.asList(doboCodes.get(0).getPeriodCode().split(",")));
			}
		}
		result.setResult(flag);
		result.setOrderItem(orderItem);
		result.setCreateTime(order.getCreateTime());
		result.setCustomrPhone(lhConfig.getCustomerPhone());
		return result;
	}

	/**
	 * 获取付款详情
	 * @return
	 */
	private String getPaymentDetails(String paymentId) {
		try{
			URL url = new URL(lhConfig.getPaypal_payment_detail()+paymentId);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET");// 提交模式
			//设置请求头header
			conn.setRequestProperty("Accept", "application/json");
			conn.setRequestProperty("Authorization", "Bearer "+getAccessToken());
			InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
			BufferedReader reader = new BufferedReader(inStream);
			StringBuilder result = new StringBuilder();
			String lineTxt = null;
			while((lineTxt = reader.readLine()) != null){
				result.append(lineTxt);
			}
			reader.close();
			return result.toString();
		}catch(Exception err){
			log.error("Exception:{}", err);
		}
    	return null;
	}

	/**
	* 获取token
	* @return
	*/
	  private String getAccessToken(){
		try{
			URL url = new URL(lhConfig.getPaypal_token_url());
			String authorization = lhConfig.getPaypal_client_id()+":"+lhConfig.getPaypal_secret();
			authorization = Base64.encodeBase64String(authorization.getBytes());

			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST");// 提交模式
			//设置请求头header
			conn.setRequestProperty("Accept", "application/json");
			conn.setRequestProperty("Accept-Language", "en_US");
			conn.setRequestProperty("Authorization", "Basic "+authorization);
			// conn.setConnectTimeout(10000);//连接超时 单位毫秒
			// conn.setReadTimeout(2000);//读取超时 单位毫秒
			conn.setDoOutput(true);// 是否输入参数
			String params = "grant_type=client_credentials";
			conn.getOutputStream().write(params.getBytes());// 输入参数

			InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
			BufferedReader reader = new BufferedReader(inStream);
			StringBuilder result = new StringBuilder();
			String lineTxt = null;
			while((lineTxt = reader.readLine()) != null){
				result.append(lineTxt);
			}
			reader.close();
			String accessTokey = JSONObject.fromObject(result.toString()).optString("access_token");
			return accessTokey;
		}catch(Exception err){
			log.error("Exception:{}",err);
		}
		return null;
  }



	@Override
	public void cancel(String orderItem, Long userId) {
		QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq(Constant.ORDER_ITEM, orderItem).eq(Constant.USER_ID,userId);
		Order order = baseMapper.selectOne(queryWrapper);

		if(!OrderStatusEnum.INPAY.equals(order.getOrderStatus())){
			return;
		}
		Order orderUpdate = new Order();
		orderUpdate.setOid(order.getOid());
		orderUpdate.setUpdateTime(new Date());
		orderUpdate.setOrderStatus(OrderStatusEnum.TOPAY);

		baseMapper.updateByKey(orderUpdate);//更新ORDER订单
	}

	@Override
	@Transactional
	public void orderPayCallback(String jsonStr) {
	  	log.info("orderPayCallback_josn:{}",jsonStr);
		JSONObject jsonObject = JSONObject.fromObject(jsonStr);
		String eventType = jsonObject.getString("event_type");
		//支付成功回调
		if("PAYMENT.SALE.COMPLETED".equals(eventType)){
             paySuccess(jsonObject);
        }else if("PAYMENT.SALE.REFUNDED".equals(eventType)){
		    orderRefund(jsonObject);
        }

	}

    /**
     　* @description: 处理
     　* @author GaoZien
     　* @date 2020/5/27 0027
     */
    private void orderRefund(JSONObject jsonObject) {
		log.info("PAYMENT.SALE.REFUNDED,starting");
		JSONObject resource = jsonObject.getJSONObject("resource");
		String paymentId = resource.getString("parent_payment");
		String saleId = resource.getString("id");
		String state = resource.getString("state");
		log.info("PAYMENT.SALE.REFUNDED,paymentId:{},saleId:{}", paymentId, saleId);

		String str = getPaymentDetails(paymentId);
		JSONObject detail = JSONObject.fromObject(str);
		JSONObject transaction = detail.getJSONArray(TRANSACTIONSKEY).getJSONObject(0);
		String orderItem  = transaction.getString("custom");
		log.info("PAYMENT.SALE.COMPLETED,paymentId:{}, saleId:{}, orderItem:{}", paymentId, saleId, orderItem);
		if(StringUtils.isBlank(orderItem)){
			return;
		}
		Order order = baseMapper.selectByItem(orderItem);
		if(order != null){
			refundOrder(order,state);
			return;
		}

		OrderAfterSale orderAfterSale = orderAfterSaleMapper.selectByItem(orderItem);
		if(orderAfterSale == null){
			log.info("PAYMENT.SALE.REFUNDED,orderAfterSale is null!!");
			return;
		}
		if(!AfterStatusEnum.REFUNDING.equals(orderAfterSale.getStatus()) && !AfterStatusEnum.PROCESSING.equals(orderAfterSale.getStatus())){
			log.info("PAYMENT.SALE.REFUNDED,order_status,error orderAfterSale:{}",orderAfterSale);
			return;
		}
		orderAfterSaleMapper.updateAfterSaleOrder(orderAfterSale.getAfterSaleId(), AfterTypeEnum.REFUND.equals(orderAfterSale.getType())?AfterStatusEnum.REFUNDSUCCESS:AfterStatusEnum.RETURNSUCCESS);
		orderAfterSaleLogMapper.insertOrderLog(orderAfterSale.getUserId(),orderAfterSale.getAfterSaleId(),orderAfterSale.getStatus(),AfterTypeEnum.REFUND.equals(orderAfterSale.getType())?AfterStatusEnum.REFUNDSUCCESS:AfterStatusEnum.RETURNSUCCESS, RemarkEnum.SHZT00);
		log.info("PAYMENT.SALE.REFUNDED,afterSaleOrder success");
    }

	private void refundOrder(Order order,String state) {
		if(!OrderStatusEnum.ACTIVITYLOSE.equals(order.getOrderStatus())){
			log.info("PAYMENT.SALE.REFUNDED,order_status,error orderItem:{}",order.getOrderItem());
			return ;
		}

		//创建订单状态日志
		Order orderUp = new Order();
		orderUp.setOid(order.getOid());
		orderUp.setUpdateTime(new Date());
		orderUp.setOrderStatus(OrderStatusEnum.REFUNDED);
		String remark= RemarkEnum.SHZT24;

		if(!"completed".equals(state)){
			remark= RemarkEnum.SHZT25;
			orderUp.setOrderStatus(OrderStatusEnum.REFUNDING);
		}

		orderUp.setSignTime(new Date());
		baseMapper.updateByKey(orderUp);
		addStatusLog(order.getUserId(), order.getOid(), order.getOrderStatus(), orderUp.getOrderStatus(), remark);
		log.info("PAYMENT.SALE.REFUNDED,order success");
	}

	/**
	　* @description: 支付成功处理
	　* @author GaoZien
	　* @date 2020/5/27 0027
	*/
    private void paySuccess(JSONObject jsonObject) {
		log.info("PAYMENT.SALE.COMPLETED,starting");
		try {
			JSONObject resource = jsonObject.getJSONObject("resource");
			String paymentId = resource.getString("parent_payment");
			String saleId = resource.getString("id");
			log.info("PAYMENT.SALE.COMPLETED,paymentId:{},saleId:{}", paymentId, saleId);
			String orderItem = resource.getString("custom");
			log.info("PAYMENT.SALE.COMPLETED,paymentId:{}, saleId:{}, orderItem:{}", paymentId, saleId, orderItem);
			if(StringUtils.isBlank(orderItem)){
				return;
			}
			Order order = baseMapper.selectByItem(orderItem);
			if(order == null || !OrderStatusEnum.TOPAY.equals(order.getOrderStatus())){
				return ;
			}
			updateOrderSuccess(order, saleId, paymentId);
		} catch (Exception e) {
			log.error("paySuccess_Exception:{}",e);
		}

	}
}
